/*
 * Copyright 2025 mingliqiye
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ProjectName mingli-utils
 * ModuleName mingli-utils.main
 * CurrentFile UUID.java
 * LastUpdate 2025-09-10 11:14:27
 * UpdateUser MingLiPro
 */

package com.mingliqiye.utils.uuid;

import com.github.f4b6a3.uuid.UuidCreator;
import com.mingliqiye.utils.string.StringUtil;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.DateTimeOffset;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
import java.util.Objects;
import lombok.Data;

/**
 * UUID 工具类，用于生成、解析和操作 UUID。
 * 支持时间戳型 UUID（版本1）以及标准 UUID 的创建与转换。
 *
 * @author MingLiPro
 */
@Data
public class UUID implements Serializable {

	/**
	 * 内部封装的 java.util.UUID 实例
	 */
	private java.util.UUID uuid;

	/**
	 * 构造一个由指定高位和低位组成的 UUID。
	 *
	 * @param msb 高64位
	 * @param lsb 低64位
	 */
	public UUID(long msb, long lsb) {
		uuid = new java.util.UUID(msb, lsb);
	}

	/**
	 * 构造一个基于当前时间的时间戳型 UUID（版本1）。<br>
	 * 由于springboot 默认使用此构造函数构造UUID 导致致命BUG 废弃<br>
	 * 下个大版本删除<br>
	 * @deprecated 请使用 <code>UUID.getTimeBased()</code>
	 */
	@Deprecated
	public UUID() {
		uuid = UUID.getTimeBased().GetUUID();
	}

	/**
	 * 使用给定的 java.util.UUID 对象构造一个新的 UUID 实例。
	 *
	 * @param uuid java.util.UUID 实例
	 */
	public UUID(java.util.UUID uuid) {
		this.uuid = uuid;
	}

	/**
	 * 根据字符串表示的 UUID 构造一个新的 UUID 实例。
	 *
	 * @param uuid 字符串形式的 UUID
	 */
	public UUID(String uuid) {
		this.uuid = java.util.UUID.fromString(uuid);
	}

	/**
	 * 获取一个基于当前时间生成的时间戳型 UUID（版本1）。
	 *
	 * @return 时间戳型 UUID
	 */
	public static UUID getTimeBased() {
		return new UUID(UuidCreator.getTimeBased());
	}

	/**
	 * 将字节数组转换为 UUID 实例。
	 *
	 * @param bytes 表示 UUID 的 16 字节数据
	 * @return 新建的 UUID 实例
	 */
	public static UUID of(byte[] bytes) {
		if (bytes == null) {
			return null;
		}
		ByteBuffer bb = ByteBuffer.wrap(bytes);
		long msb = bb.getLong();
		long lsb = bb.getLong();
		return new UUID(msb, lsb);
	}

	/**
	 * 将字符串解析为 UUID 实例，如果解析失败则抛出 UUIDException。
	 *
	 * @param data UUID 字符串
	 * @return 解析后的 UUID 实例
	 * @throws UUIDException 如果解析失败
	 */
	public static UUID of(String data) {
		if (data == null) {
			return null;
		}
		try {
			return new UUID(java.util.UUID.fromString(data));
		} catch (Exception e) {
			throw new UUIDException(e.getMessage(), e);
		}
	}

	/**
	 * 将 UUID 转换为 16 字节的字节数组。
	 *
	 * @return 表示该 UUID 的字节数组
	 */
	public byte[] toBytes() {
		if (this.uuid == null) {
			return null;
		}
		ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
		bb.putLong(uuid.getMostSignificantBits());
		bb.putLong(uuid.getLeastSignificantBits());
		return bb.array();
	}

	/**
	 * 获取内部封装的 java.util.UUID 实例。
	 *
	 * @return java.util.UUID 实例
	 */
	public java.util.UUID GetUUID() {
		return uuid;
	}

	/**
	 * 将 UUID 转换为字符串表示，默认使用小写格式。
	 *
	 * @return UUID 字符串
	 */
	public String toUUIDString() {
		return toUUIDString(false);
	}

	/**
	 * 将 UUID 转换为字符串表示，并可选择是否使用大写。
	 *
	 * @param u 是否使用大写格式
	 * @return UUID 字符串
	 * @throws UUIDException 如果 uuid 为 null
	 */
	public String toUUIDString(boolean u) {
		if (uuid == null) {
			throw new UUIDException("uuid is null : NullPointerException");
		}
		if (u) {
			return uuid.toString().toUpperCase(Locale.ROOT);
		}
		return uuid.toString();
	}

	/**
	 * 计算此 UUID 的哈希码。
	 *
	 * @return 哈希码值
	 */
	@Override
	public int hashCode() {
		return Objects.hash(uuid);
	}

	/**
	 * 判断两个 UUID 是否相等。
	 *
	 * @param o 比较对象
	 * @return 如果相等返回 true，否则返回 false
	 */
	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}
		UUID uuid = (UUID) o;
		return Objects.equals(this.uuid, uuid.uuid);
	}

	/**
	 * 返回此 UUID 的字符串表示，包含版本信息和时间戳（如果是版本1）。
	 *
	 * @return UUID 的详细字符串表示
	 */
	@Override
	public String toString() {
		if (uuid == null) {
			return "UUID(null)";
		}
		return StringUtil.format(
			"UUID(uuid={},version={})",
			toUUIDString(true),
			uuid.version()
		);
	}

	/**
	 * 从时间戳型 UUID 中提取时间戳并转换为 DateTime 对象。
	 *
	 * @return 对应的 DateTime 对象；如果 uuid 为 null，则返回 null
	 */
	public DateTime getDateTime() {
		if (uuid == null) {
			return null;
		}
		if (uuid.version() != 1) {
			return null;
		}
		return DateTime.of(uuid.timestamp() / 10_000).add(
			DateTimeOffset.of(-141427L, ChronoUnit.DAYS)
		);
	}

	/**
	 * 从时间戳型 UUID 中提取 MAC 地址，默认使用冒号分隔符。
	 *
	 * @return MAC 地址字符串
	 * @throws UUIDException 如果 uuid 为 null
	 */
	public String extractMACFromUUID() {
		return extractMACFromUUID(null);
	}

	/**
	 * 从时间戳型 UUID 中提取 MAC 地址，并允许自定义分隔符。
	 *
	 * @param spec 分隔符字符，默认为 ":"
	 * @return MAC 地址字符串
	 * @throws UUIDException 如果 uuid 为 null
	 */
	public String extractMACFromUUID(String spec) {
		if (uuid == null) {
			throw new UUIDException("uuid is null : NullPointerException");
		}
		if (spec == null) {
			spec = ":";
		}
		long leastSigBits = uuid.getLeastSignificantBits();
		long macLong = leastSigBits & 0xFFFFFFFFFFFFL;
		byte[] macBytes = new byte[6];
		// 提取 MAC 地址的每个字节
		for (int i = 0; i < 6; i++) {
			macBytes[5 - i] = (byte) (macLong >> (8 * i));
		}
		StringBuilder mac = new StringBuilder();
		// 构造 MAC 地址字符串
		for (int i = 0; i < 6; i++) {
			mac.append(String.format("%02X", macBytes[i]));
			if (i < 5) {
				mac.append(spec);
			}
		}
		return mac.toString();
	}
}
